
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Configuration;
using System.Xml;
using Swaf;
using Swaf.Call;
using Swaf.Config;
using Swaf.Container;
using Swaf.Instrumentation;

namespace Swaf.Security
{
	/// <summary>
	/// The SecurityManager class provides all of the base functionality for security 
	/// configuration handling, concrete implementations of createPrincipal method and 
	/// resourceSets and currentPrincipal properties and serves as the root element in 
	/// the security object model.  This class is instantiated and exposed directly from 
	/// the Application object (the global application object).
	/// 
	/// The SecurityManagerClass provides the hooks into the security sub-system within
	/// CSF.  This class provides the factory for creating new security principals and
	/// maintains a reference to the current security principal as well as base list of
	/// available resourceSets.
	/// 
	/// </summary>
	public class SecurityManager : MarshalByRefObject,ISecurityManager, IConfigPartner
	{
		protected ISecurityPrincipal m_currentPrincipal=null;
		protected INameAddressSupport m_resourceSets = new FlexiMap();
		protected string m_securityPrincipalClass=null;
		protected ICall m_rolesCall=null;
		protected bool m_deepCloneResourceSets=false;

		/// <summary>
		/// Default constructor for SecurityManager class.
		/// </summary>
		public SecurityManager()
		{
		}
		#region ISecurityManager Members

		/// <summary>
		/// <see cref="ISecurityManager.currentPrincipal"/>
		/// </summary>
		public ISecurityPrincipal currentPrincipal
		{
			get
			{
				return m_currentPrincipal;
			}
			set
			{
				m_currentPrincipal = value;
			}
		}

		/// <summary>
		/// <see cref="ISecurityManager.resourceSets"/>
		/// </summary>
		public Swaf.Container.INameAddressSupport resourceSets
		{
			get
			{
				return m_resourceSets;
			}
		}

		/// <summary>
		/// Creates and returns a new instance of an ISecurityPrincipal object.  This method requires
		/// that a SecurityPrincipal configuration node be present somewhere in the applicaton's config
		/// files.  BaseFrameworkFeature defines a default principal that is basically an noop.
		/// <see cref="ISecurityManager.createPrincipal"/>
		/// </summary>
		/// <param name="principalInfo">This INameAddressSupport parameter MUST provide at a minimum
		/// an entry named UserName of type string.  Any additional entries needed by the the
		/// system using the security module are acceptable.</param>
		/// <returns></returns>
		public ISecurityPrincipal createPrincipal(Swaf.Container.INameAddressSupport principalInfo)
		{
			ISecurityPrincipal principal = (ISecurityPrincipal) Cmn.callCtor(m_securityPrincipalClass,new Type[]{typeof(string), typeof(INameAddressSupport), typeof(ICall)},new object[]{(string) principalInfo.get("UserName"), principalInfo, m_rolesCall});
			principal.deepCloneResourceSets=m_deepCloneResourceSets;
			return principal;
		}

		
		/// <summary>
		/// <see cref="ISecurityManager.createPrincipal"/>
		/// </summary>
		/// <param name="UserName"></param>
		/// <param name="additionalInfo"></param>
		/// <param name="rolesCall"></param>
		/// <returns></returns>
		public ISecurityPrincipal createPrincipal(string UserName, INameAddressSupport additionalInfo, ICall rolesCall)
		{
			ISecurityPrincipal principal = (ISecurityPrincipal) Cmn.callCtor(m_securityPrincipalClass,new Type[]{typeof(string), typeof(INameAddressSupport), typeof(ICall)},new object[]{UserName, additionalInfo, rolesCall == null?m_rolesCall:rolesCall});
			principal.deepCloneResourceSets=m_deepCloneResourceSets;
			return principal;
		}
		/// <summary>
		/// <see cref="ISecurityManager.createPrincipal"/>
		/// </summary>
		/// <param name="principalEle"></param>
		/// <returns></returns>
		public ISecurityPrincipal createPrincipal(XmlElement principalEle)
		{
			ISecurityPrincipal principal = (ISecurityPrincipal) Cmn.callCtor(m_securityPrincipalClass,new Type[]{principalEle.GetType()},new object[]{principalEle});
			principal.deepCloneResourceSets=m_deepCloneResourceSets;
			return principal;
		}
		
		/// <summary>
		/// Called from the config handler for resourceset node to add the resource set to the cache
		/// and to the default principal if present.
		/// </summary>
		/// <param name="newSet"></param>
		protected void addResourceSet(IResourceSet newSet)
		{
			m_resourceSets.put(newSet.name,  newSet);
			if (m_currentPrincipal != null)
				m_currentPrincipal.resourceSets.put(newSet.name,  newSet);
		}

		/// <summary>
		/// <see cref="ISecurityManager.defaultRolesCall"/>
		/// </summary>
		public ICall defaultRolesCall
		{
			get{return m_rolesCall;}	
		}
		public void setCurrentPrincipal(string UserName, INameAddressSupport additionalInfo, string instanceId)
		{
			m_currentPrincipal=createPrincipal(UserName,additionalInfo,m_rolesCall);
			((SecurityPrincipalBase) m_currentPrincipal).m_instanceId=instanceId;
		}
		#endregion

		#region IConfigPartner Members

		/// <summary>
		/// <see cref="Swaf.Config.IConfigPartner.checkConfigStatus"/>
		/// </summary>
		public void checkConfigStatus()
		{
			if (m_securityPrincipalClass == null)
			{
				throw new ConfigurationException("No SecurityPrincipal configuration entry was found.  Please make sure you have the latest BaseFrameworkFeature file loaded.");
			}
			//Set the static reference to the FailedSecurityAssertions counter.
			IAppInstrumentation instMgr = Application.currentApp.appData["sysObjs-AppInstrumentation"] as IAppInstrumentation;
			if (instMgr != null)
				AccessDeniedException.s_securityCounter=instMgr.registerCounterHandler("FailedSecurityAssertions", "counter");
		}

		/// <summary>
		/// <see cref="Swaf.Config.IConfigPartner.registerForConfig"/>
		/// </summary>
		/// <param name="config"></param>
		public void registerForConfig(Swaf.Config.IConfigManager config)
		{
			config.registerHandler("...<SecurityPrincipal>", new SecurityPrincipalConfigHandler(this));
			config.registerHandler("...<DefaultPrincipal>", new DefaultPrincipalConfigHandler(this));
			//config.registerHandler("...<ResourceSets> <ResourceSet>", new SecurityResourceSetConfigHandler(this));
			config.registerHandler("...<ResourceSets>", new SecurityResourceSetConfigHandler(this));
			config.registerHandler("...<RolesCall>", new SecurityRolesCallConfigHandler(this));
		}
		
		/// <summary>
		/// Handler class for Security RolesCall configuration nodes
		/// </summary>
		public class SecurityRolesCallConfigHandler : ConfigHandler
		{
			/// <summary>
			/// Constructor called by registerForConfig
			/// </summary>
			/// <param name="parent">Reference to the containing SecurityManager</param>
			public SecurityRolesCallConfigHandler(SecurityManager parent)
			{
				m_mgr = parent;			
			}
			/// <summary>
			/// <see cref="Swaf.Config.ConfigHandler.executeBefore"/>
			/// </summary>
			/// <param name="configInfo"></param>
			public override void  executeBefore(IConfigElement configInfo)
			{
				XmlNode parent = configInfo.Element;
				string name=configInfo.getAttribute("name", null);
				foreach(XmlNode node in parent.ChildNodes)
				{
					if (node.NodeType == XmlNodeType.Element)
					{
						m_mgr.m_rolesCall = (ICall) Application.currentApp.factories["CallTypes"].createFromNickName(node.Name,name,(XmlElement) node);
						Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL,"Set RolesCall instance to {0}",node.OuterXml);
						break;
					}
				}
			}
			private SecurityManager m_mgr;
		}

		/// <summary>
		/// Handler class for Security DefaultPrincipal configuration nodes
		/// </summary>
		public class DefaultPrincipalConfigHandler : ConfigHandler
		{
			/// <summary>
			/// Constructor called by registerForConfig
			/// </summary>
			/// <param name="parent">Reference to the containing SecurityManager</param>
			public DefaultPrincipalConfigHandler(SecurityManager parent)
			{
				m_mgr = parent;			
			}
			/// <summary>
			/// <see cref="Swaf.Config.ConfigHandler.executeBefore"/>
			/// </summary>
			/// <param name="configInfo"></param>
			public override void  executeBefore(IConfigElement configInfo)
			{
				m_mgr.m_currentPrincipal = m_mgr.createPrincipal(configInfo.Element);
				Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL,"Set Default Security Principal to user {0}", m_mgr.currentPrincipal.userName);
			}

			private SecurityManager m_mgr;
		}

		/// <summary>
		/// Handler class for Security SecurityPrincipal configuration nodes
		/// </summary>
		public class SecurityPrincipalConfigHandler : ConfigHandler
		{
			/// <summary>
			/// Constructor called by registerForConfig
			/// </summary>
			/// <param name="parent">Reference to the containing SecurityManager</param>
			public SecurityPrincipalConfigHandler(SecurityManager parent)
			{
				m_mgr = parent;			
			}
			/// <summary>
			/// <see cref="Swaf.Config.ConfigHandler.executeBefore"/>
			/// </summary>
			/// <param name="configInfo"></param>
			public override void  executeBefore(IConfigElement configInfo)
			{
				m_mgr.m_securityPrincipalClass = configInfo.getAttribute("class");
				
				string deepParse=configInfo.getAttribute("deepCloneResourceSets","false");
				m_mgr.m_deepCloneResourceSets=bool.Parse(deepParse);
				
				//If the default principal has been set already, make sure to change its deepCloneResourceSets setting.
				if (m_mgr.currentPrincipal != null)
				{
					m_mgr.currentPrincipal.deepCloneResourceSets=m_mgr.m_deepCloneResourceSets;
				}
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"Defined Security Principal class {0}", m_mgr.m_securityPrincipalClass);
			}

			private SecurityManager m_mgr;
		}

		/// <summary>
		/// Handler class for Security ResourceSet configuration nodes
		/// </summary>
		public class SecurityResourceSetConfigHandler : ConfigHandler
		{
			/// <summary>
			/// Constructor called by registerForConfig
			/// </summary>
			/// <param name="parent">Reference to the containing SecurityManager</param>
			public SecurityResourceSetConfigHandler(SecurityManager parent)
			{
				m_mgr = parent;			
			}
			/// <summary>
			/// <see cref="Swaf.Config.ConfigHandler.executeBefore"/>
			/// </summary>
			/// <param name="configInfo"></param>
			public override void  executeBefore(IConfigElement configInfo)
			{
				foreach (XmlNode node in configInfo.Element.ChildNodes)
				{
					if (node is XmlElement)
					{
						XmlElement el = (XmlElement)node;
						//Use the ResourceSets class factory to instantiate the appropriate ResourceSet implementation.
						IResourceSet resSet = (IResourceSet)Application.currentApp.factories["ResourceSets"].createFromNickName(el.Name, el);
						m_mgr.addResourceSet(resSet);
						Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL, "Defined ResourceSet {0}", resSet.name);
					}
				}
			}

			private SecurityManager m_mgr;
		}
		#endregion
	}
}
